//	Win32 Memory Allocation
////////////////////////////////////////////////////////////////////////////////////////////////////
#include "stdafx.h"

#include <stdlib.h>
#include <assert.h>
#include <malloc.h>
#include <memory.h>
#include <stdio.h>

#include "Core\Commons.h"

#include "Memory_Win32.h"

////////////////////////////////////////////////////////////////////////////////////////////////////

void CheckHeap();

////////////////////////////////////////////////////////////////////////////////////////////////////

#ifdef MEMORY_DEBUG	


extern bool _start_trace_memory;

struct AllocInfo
{
	size_t size;
	void* ptr;
	bool free;

	AllocInfo()
		:ptr(0),size(-1),free(true)
	{
	}

	void set(void* ptr, size_t size)
	{
		this->ptr = ptr;
		this->size = size;
		this->free = false;
	}

	void reset()
	{
		ptr = 0;
		size = -1;
		free = true;
	}
};

class AllocMap
{
public:
	~AllocMap(){}

	static AllocMap& instance()
	{
		static AllocMap s_instance;
		return s_instance;
	}

	void allocate(void* ptr, size_t size)
	{		
		unsigned int hashCode = reinterpret_cast<unsigned int>(ptr)%k_maxAllocs;

		bool monitored = false;

		do
		{
			for(int i=hashCode; i<k_maxAllocs; ++i)
			{
				if(m_allocs[i].free)
				{
					m_allocs[i].set(ptr,size);					
					monitored = true;
					break;
				}
			}

			if(monitored)
				break;

			for(int i=0; i<hashCode; ++i)
			{
				if(m_allocs[i].free)
				{
					m_allocs[i].set(ptr,size);					
					monitored = true;
					break;
				}
			}
		}
		while(false);

		if(monitored)
		{
			++m_allocCount;
			m_allocBytes += size;
		}
		else
		{
			++m_nonMonitoredCount;
			m_nonMonitoredBytes+=size;
		}

		int totalAlloc = m_allocCount+m_nonMonitoredCount;
		if(totalAlloc>m_maxTotalAllocCount)
		{
			m_maxTotalAllocCount = totalAlloc;
		}

		size_t totalAllocBytes = m_allocBytes+m_nonMonitoredBytes;
		if(totalAllocBytes>m_maxTotalBytes)
		{
			m_maxTotalBytes = totalAllocBytes;
		}
	}

	void deallocate(void* ptr)
	{
		if(ptr==0)
			return;

		unsigned int hashCode = reinterpret_cast<unsigned int>(ptr)%k_maxAllocs;

		for(int i=hashCode; i<k_maxAllocs; ++i)
		{
			if(!m_allocs[i].free && m_allocs[i].ptr==ptr)
			{
				m_allocs[i].free = true;
				--m_allocCount;
				m_allocBytes -= m_allocs[i].size;
				return;
			}
		}

		for(int i=0; i<hashCode; ++i)
		{
			if(!m_allocs[i].free && m_allocs[i].ptr==ptr)
			{
				m_allocs[i].free = true;
				--m_allocCount;
				m_allocBytes -= m_allocs[i].size;
				return;
			}
		}

		--m_nonMonitoredCount;
		size_t size = _msize(ptr);
		m_nonMonitoredBytes-=size;
	}

	void reset()
	{
		for(int i=0; i<k_maxAllocs; ++i)
		{
			m_allocs[i].reset();
		}

		m_allocCount = 0;
		m_allocBytes = 0;
		m_nonMonitoredCount = 0;
		m_nonMonitoredBytes = 0;
		m_maxTotalAllocCount = 0;
		m_maxTotalBytes = 0;
	}

	/////////////////////////
	
	void logMemoryInfo()
	{
		size_t currentTotalMemoryBytes = (m_allocBytes+m_nonMonitoredBytes);
		static size_t s_lastCallTotalMemoryBytes = currentTotalMemoryBytes;

		LOG("memory", "\tCurrent total alloc count = %d, nonMonitored(%d)", m_allocCount+m_nonMonitoredCount, m_nonMonitoredCount);
		LOG("memory", "\tMax total alloc count = %d", m_maxTotalAllocCount);
		LOG("memory", "\tCurrent total allocated memory = %.5fM, nonMonitored(%.5fM)", currentTotalMemoryBytes/(1024*1024.f), m_nonMonitoredBytes/(1024*1024.f));
		LOG("memory", "\tMax total allocated memory = %.5fM\n", m_maxTotalBytes/(1024*1024.f));					
		LOG("memory", "\tTotal memory differs from last log = %d(bytes)\n", currentTotalMemoryBytes-s_lastCallTotalMemoryBytes);	

		s_lastCallTotalMemoryBytes = currentTotalMemoryBytes;
	}	

	void printLeaks()
	{	
		LOG("memory", "\n");
		LOG("memory", "Memory stats:\n");		
		
		logMemoryInfo();

		int leakNum = 0;
		size_t leakSize = 0;
							
		for(int i=0; i<k_maxAllocs; ++i)
		{
			if(!m_allocs[i].free)
			{				
				LOG("memory", "\tleak address: %d, size: %dBytes" , m_allocs[i].ptr, m_allocs[i].size);				
				leakNum++;
				leakSize+=m_allocs[i].size;
			}
		}

		if(m_nonMonitoredCount>0)
		{
			LOG("memory", "\tNonmonitored leak count= %d, total size= %d Bytes",m_nonMonitoredCount,m_nonMonitoredBytes);
			leakNum+=m_nonMonitoredCount;
			leakSize+=m_nonMonitoredBytes;
		}
		

		if(leakNum>0)
		{
			LOG("memory", "\tTotal %d memory blocks leaks, %d bytes\n",leakNum,leakSize);
		}
		else
		{
			LOG("memory", "\tNo memory blocks leaks.\n");
		}


		LOG("memory", "\n");

	}


private:
	AllocMap()
	{
		reset();
	}

	static const unsigned k_maxAllocs= 120000;
	AllocInfo m_allocs[k_maxAllocs];	

	int m_allocCount;
	size_t m_allocBytes;
	int m_nonMonitoredCount;
	size_t m_nonMonitoredBytes;	

	int m_maxTotalAllocCount;
	size_t m_maxTotalBytes;

	friend class AutoMemoryUsedCounter;
};


AutoMemoryUsedCounter::AutoMemoryUsedCounter()
{		
	m_totalMemoryWhenInit = AllocMap::instance().m_allocBytes + AllocMap::instance().m_nonMonitoredBytes;
}

AutoMemoryUsedCounter::~AutoMemoryUsedCounter()
{
	size_t totalMemoryCurrent = AllocMap::instance().m_allocBytes + AllocMap::instance().m_nonMonitoredBytes;
	size_t memoryDiffs = totalMemoryCurrent-m_totalMemoryWhenInit;

	LOG("memory", "\tMemory differs = %d(bytes) < %.5f(k) %.8f(M) >\n", memoryDiffs, memoryDiffs/1024.0f, memoryDiffs/(1024*1024.0f));	
}

#endif	//MEMORY_DEBUG

////////////////////////////////////////////////////////////////////////////////////////////////////

void LogMemoryInfo()
{
#ifdef MEMORY_DEBUG	

	AllocMap::instance().logMemoryInfo();

#endif
}


////////////////////////////////////////////////////////////////////////////////////////////////////

void PrintMemoryStats()
{
#ifdef MEMORY_DEBUG	

	AllocMap::instance().printLeaks();

#endif
}

////////////////////////////////////////////////////////////////////////////////////////////////////

void* operator new(unsigned int size)
{
	assert(size != 0);

	void* ptr = malloc(size);

#if defined( MEMORY_DEBUG )
	if (ptr == NULL)
		return NULL;
	memset(ptr, 0, size);
	if(_start_trace_memory)
		AllocMap::instance().allocate(ptr,size);	
#endif //MEMORY_DEBUG

	return ptr;
}

////////////////////////////////////////////////////////////////////////////////////////////////////

void * operator new[]( unsigned int cb )
{
	void *res = operator new(cb);

	return res;
}

////////////////////////////////////////////////////////////////////////////////////////////////////

void operator delete(void *ptr)
{
#ifdef MEMORY_DEBUG

	if(_start_trace_memory)
		AllocMap::instance().deallocate(ptr);
	
#endif //MEMORY_DEBUG	

	free(ptr);
}

////////////////////////////////////////////////////////////////////////////////////////////////////

void operator delete[]( void * p )
{
	operator delete(p);
}

////////////////////////////////////////////////////////////////////////////////////////////////////

void CheckHeap()
{
#ifdef MEMORY_DEBUG	

	_HEAPINFO hinfo;
	int heapstatus;
	hinfo._pentry = NULL;
	while( ( heapstatus = _heapwalk( &hinfo ) ) == _HEAPOK )
	{
	}

	if (heapstatus != _HEAPEND)
	{
		LOG("memory", "%6s block at %Fp of size %4.4X\n", ( hinfo._useflag == _USEDENTRY ? "USED" : "FREE" ), hinfo._pentry, hinfo._size);
	}

#endif	//MEMORY_DEBUG
}



////////////////////////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////